﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Programmer_1
{
    class TempAlarm
    {
        public delegate void MinTempAlarmEventHandler(int temp, DateTime minTempTime);
        public event MinTempAlarmEventHandler MinTempAlarm;

        public delegate void MaxTempAlarmEventHandler(int temp, DateTime maxTempTime);
        public event MaxTempAlarmEventHandler MaxTepmAlarm;

        public delegate void StateChangedEventHandler(State oldState, State newState);
        public event StateChangedEventHandler StateChanged;

        public enum State { TURNED_ON = 0x01, TURNED_OFF = 0x02 }
        State currentState = State.TURNED_OFF;
        public State CurrentState { get { return currentState; } }

        AppModel app;
        BlockingCollection<int> dataQueue;

        volatile bool turnOff;
        volatile bool connected;

        public TempAlarm(AppModel app)
        {
            this.app = app;
            app.Connected += OnProgrammerConnected;
            app.Disconnected += OnProgrammerDisconnected;
        }

        public void TurnOnAsync(int minTemp, int maxTemp, string filename)
        {
            dataQueue = new BlockingCollection<int>(100);//creating new as previous was made completed
            // subscribe
            app.DataRead += EnqueDataRead;
            Task.Run(() =>
            {
                _TurnOn(minTemp, maxTemp, filename);
            });
        }

        void EnqueDataRead(int temp, int light, int rate)
        {
            // light is better for testing
            dataQueue.TryAdd(light);// non-blocking, possible loss of data?
        }

        void _TurnOn(int minTemp, int maxTemp, string filename)
        {
            turnOff = false;
            SetState(State.TURNED_ON);
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(filename))
            {
                file.WriteLine("Data logging started: [" + DateTime.Now.ToString("yyyy-MM-dd H-mm-ss") + "]");
                // TODO: logging stopped
                // TODO: samples collected
                file.WriteLine("-----------------------------------------------------------------------------");
                file.WriteLine("#,      Date/Time,      Temperature,        Min Level,      Max Level");

                for (int i = 1; !turnOff; i++)
                {
                    try
                    {
                        int temp = dataQueue.Take(); // blocking
                        if (temp <= minTemp || temp >= maxTemp)
                        {
                            // notify UI
                            if (temp <= minTemp)
                            {
                                this.MinTempAlarm(temp, DateTime.Now);
                            }
                            else
                            {
                                this.MaxTepmAlarm(temp, DateTime.Now);
                            }
                            // log to file
                            file.WriteLine("" + i + ",      "
                                + DateTime.Now.ToString("yyyy-MM-dd H-mm-ss-fff") + ",      "
                                + temp + ",     " + minTemp + ",        " + maxTemp);
                        }
                    }
                    catch (InvalidOperationException ex)
                    {
                        Debug.WriteLine("Exception: {0}", ex);
                    }
                }
            }
            SetState(State.TURNED_OFF);
        }

        public void TurnOff()
        {
            // unsubscribe
            app.DataRead -= EnqueDataRead;
            turnOff = true;
            dataQueue.CompleteAdding();
        }

        void SetState(State newState)
        {
            if (!connected)
            {
                newState = State.TURNED_OFF;
            }
            State oldState = this.currentState;
            this.currentState = newState;
            if (oldState != newState)
            {
                StateChanged(oldState, newState);
            }
        }

        void OnProgrammerConnected(string progID)
        {
            connected = true;
        }

        void OnProgrammerDisconnected(string progID)
        {
            connected = false;
            SetState(State.TURNED_OFF);
        }
    }
}
